#!usr/bin/env python3
# -*- coding:utf-8 -*-
# 实践项目 简单的文本、代码编辑器
# https://zhuanlan.zhihu.com/p/48373518
# https://blog.csdn.net/xiaoyangyang20/article/details/68923133 这里是可以语法高亮的编辑器


import sys
from PyQt5.QtWidgets import QApplication,QMainWindow,QPlainTextEdit,QWidget,QTextEdit
from PyQt5.QtCore import QRect
from PyQt5.QtGui import QFont,QColor,QTextFormat,QPainter,QFontMetrics,QSyntaxHighlighter
from PyQt5.Qt import Qt

class QCodeEditor(QPlainTextEdit):
    class NumberBar(QWidget):
        def __init__(self,editor):
            QWidget.__init__(self,editor) #
            self.editor = editor
            self.editor.blockCountChanged.connect(self.updateWidth) #每当块数（段落）发生变化时都会发出该信号
            self.editor.updateRequest.connect(self.updateContents) #当文本文档需要更新指定的矩形时，会发出此信号。如果文字滚动，矩形将覆盖整个视区。 如果文本垂直滚动，dy将携带视口滚动的像素数量（dy是信号给槽函数的参数）。
            self.font = QFont()
            self.barColor = QColor("#e8e8e8")
        
        def paintEvent(self,event):
            painter = QPainter(self)
            painter.fillRect(event.rect(),self.barColor)
            block = self.editor.firstVisibleBlock()
            while block.isValid():
                blockNum = block.blockNumber()
                block_top = self.editor.blockBoundingGeometry(block).translated(self.editor.contentOffset()).top()
                if blockNum==self.editor.textCursor().blockNumber():
                    self.font.setBold(True)
                    painter.setPen(QColor("#000000"))
                else:
                    self.font.setBold(False)
                    painter.setPen(QColor("#717171"))
                paint_rect = QRect(0,block_top,self.width(),self.editor.fontMetrics().height())
                #print("paint_rect",paint_rect)
                painter.drawText(paint_rect, Qt.AlignCenter, str(blockNum+1))
                block = block.next()
        
        def getWidth(self):
            count = self.editor.blockCount() #文本块的数量 也就是行数            
            if 0<=count<99999:
                width = self.fontMetrics().width("99999")
            else:
                width = self.fontMetrics().width(str(count))
            return width

        def updateWidth(self):
            w = self.getWidth()            
            self.editor.setViewportMargins(w,0,0,0)

        def updateContents(self,rect,dy):            
            if dy:
                self.scroll(0,dy)
            else:
                self.update(0,rect.y(),self.width(),rect.height())
            
            if rect.contains(self.editor.viewport().rect()):
                fontSize = self.editor.currentCharFormat().font().pointSize()
                self.font.setPointSize(fontSize)
                self.font.setStyle(QFont.StyleNormal)
                self.updateWidth()

    def __init__(self):
        super(QCodeEditor,self).__init__()
        self.setGeometry(350,200,500,400)
        self.setWindowTitle("简单的代码编辑器")
        self.setFont(QFont("宋体",12))
        me = QFontMetrics(self.font()) #字体度量工具类 计算字符串空间的类
        self.setTabStopDistance(4*me.width(" ")) #设置tab键用4个空格代替
        self.setLineWrapMode(QPlainTextEdit.NoWrap)
        self.numBar = self.NumberBar(self)
        self.curLineNum = None
        self.curLineColor = None
        self.cursorPositionChanged.connect(self.hightLigtCurentLine)
        self.setViewportMargins(40,0,0,0)
        self.hightLigtCurentLine()
        
    def resizeEvent(self,e):
        #print("窗口改变")
        cr = self.contentsRect() #部件的边界区域        
        rec = QRect(cr.left(),cr.top(),self.numBar.getWidth(),cr.height())
        self.numBar.setGeometry(rec)

    def hightLigtCurentLine(self):
        newLineNum = self.textCursor().blockNumber()
        if newLineNum!=self.curLineNum:
            lineColor = self.getLineColor()
            self.curLineNum = newLineNum
            hi_selection =  QTextEdit.ExtraSelection() #QTextEdit.ExtraSelection结构提供了一种为文档中已选择指定字符格式的方法。
            hi_selection.format.setBackground(lineColor)
            hi_selection.format.setProperty(QTextFormat.FullWidthSelection,True) #设定文本的整个宽度将显示为选中状态。
            hi_selection.cursor = self.textCursor()
            hi_selection.cursor.clearSelection()
            self.setExtraSelections([hi_selection])


    def getLineColor(self):
        if not self.curLineColor:
            self.curLineColor = QColor(Qt.yellow)
            self.curLineColor.lighter(160)
        return self.curLineColor


class MyHightLight(QSyntaxHighlighter):
    def __init__(self,parent=None):
        super(MyHightLight,self).__init__(parent)

if __name__=="__main__":
    app = QApplication(sys.argv)
    win = QCodeEditor()
    win.show()    
    sys.exit(app.exec())

